// es6 --   common
// 作者 王欢
// 开始日期 2019-12-20
// ### 主要包括
// 对象浅拷贝 
// 类型判断
// 字符串空格处理
// 异步函数 错误处理函数
// 对象混入
/* 浅拷贝 */
const $copy = Object.assign;

/* 
  //严格类型判断
  $toClass(key:any);
*/
function $toClass(key) {
  return Object.prototype.toString.call(key).split(" ")[1].replace("]", "");
}
/* 
  //类型判断函数 
  $isType(target:any,type:any);
*/
function $isType(target, type) {
  let targetType = $toClass(target);
  return targetType === type;
}
/* 
  // 判断对象是否具有某个属性
  $hasOwn(obj:Object,key:String);
*/
function $hasOwn(obj,key){
  if( obj && $isType(obj,"Object") ){
    return Object.hasOwnProperty.call(obj,key);
  }else{
    throw new Error("the obj params classType is not a Object");
  }
}
/* 
  //字符串除去空格 
  $trimAll(str:String);
*/
function $trimAll(str) {
  if (str) {
      return str.replace(/\s/gm, "");
  } else {
      return str;
  }
}
/* 
  await-to https://github.com/scopsy/await-to-js
*/
function $to(promise, errorExt) {
  return promise
      .then((data) => [null, data])
      .catch((err) => {
      if (errorExt) {
          $copy(err, errorExt);
      }
      return [err, undefined];
  });
}


/* 
  此处借鉴 vue mixin里合并data源码 
  $merge(to:Object,from:Object);
*/
function $merge(to, from) {
  if (!from) return to;
  let key, toVal, fromVal;
  const keys =  Object.keys(from);
  for (let i = 0,len = keys.length; i < len; i++) {
    key = keys[i];
    // in case the object is already observed...
    if (key === '__ob__') continue;
    toVal = to[key];  // child
    fromVal = from[key]; // parent
    if (!$hasOwn(to, key)) {
      to[key] = fromVal;  //若to没有此key，添加它
    } else if (
      toVal !== fromVal &&
      $isType(toVal, "Object") &&
      $isType(fromVal, "Object")
    ) {
      //若to有此key，且不等
      //若to有此key，值非对象，否则进行深度合并
      $merge(toVal, fromVal);
    }
  }
  return to;
}
/* 防抖 */
function $debounce(fn, delay) {
  let timeout = null; // 创建一个标记用来存放定时器的返回值
  return function () {
    clearTimeout(timeout); // 每当用户输入的时候把前一个 setTimeout clear 掉
    timeout = setTimeout(() => { // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话，就不会执行 fn 函数
      fn.apply(this, arguments);
    }, delay);
  };
}
/* 节流 */
function $throttle(fn, delay) {
  let canRun = true; // 通过闭包保存一个标记
  return function () {
    if (!canRun) return; // 在函数开头判断标记是否为true，不为true则return
    canRun = false; // 立即设置为false
    setTimeout(() => { // 将外部传入的函数的执行放在setTimeout中
      fn.apply(this, arguments);
      // 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false，在开头被return掉
      canRun = true;
    }, delay);
  };
}

module.exports = {
  $copy
  ,$toClass
  ,$isType
  ,$hasOwn
  ,$trimAll
  ,$to
  ,$merge
  ,$debounce
  ,$throttle
}